package com.sxt.netty.server;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.sxt.netty.server.init.ServerChannelInitializer;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.ResourceLeakDetector;
import lombok.extern.slf4j.Slf4j;


/**
 * netty Server启动类
 * 
 * @program: SpringBoot+Netty+Redis
 * @Date: 2020/09/23 09:20
 * @Author: luolin
 * @Description:
 */
@Slf4j
@Component
public class TCPServer {
	/**
	 * NioEventLoopGroup是一个处理I / O操作的多线程事件循环。 Netty为不同类型的传输提供各种EventLoopGroup实现。
	 * 我们在此示例中实现了服务器端应用程序，因此将使用两个NioEventLoopGroup。
	 * 第一个，通常称为“老板”，接受传入连接。第二个，通常称为“工人”， 一旦老板接受连接并将接受的连接注册到工作人员，就处理被接受连接的流量。
	 * 使用了多少个线程以及它们如何映射到创建的Channels取决于EventLoopGroup实现，甚至可以通过构造函数进行配置。
	 */
	@Value("${netty.port}")
    private int port;
	//
	private EventLoopGroup bossGroup = null;
	//
	private EventLoopGroup workerGroup = null;
	
	@PostConstruct
	public void start() throws InterruptedException {
		//解决netty网关启动之后，cpu使用率达到了100%
				System.setProperty("org.jboss.netty.epollBugWorkaround", "true");
				ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.SIMPLE);// 内存泄漏检测 开发推荐PARANOID 线上SIMPLE
				// 内存泄漏检测 开发推荐 PARANOID 线上使用默认的SIMPLE即可
				// ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.valueOf(leakDetectorLevel.toUpperCase()));
				/** 针对linux优化 */
				boolean isAvailable = Epoll.isAvailable();
				/** 配置服务端的NIO线程组 */
				if (isAvailable) {
					/** 负责服务端接收客户端的连接 */
					this.bossGroup = new EpollEventLoopGroup();
					/** 负责进行SocketChannel的网络读写 */
					this.workerGroup = new EpollEventLoopGroup();
				} else {
					/** 负责服务端接收客户端的连接 */
					this.bossGroup = new NioEventLoopGroup();
					/** 负责进行SocketChannel的网络读写 */
					this.workerGroup = new NioEventLoopGroup();
				}
				// =============================分割线===================================//
				/** Netty启动Nio服务端的辅助类 */
				ServerBootstrap bootstrap = new ServerBootstrap();
				bootstrap.group(this.bossGroup, this.workerGroup);
				/** Linux平台用Epoll模式 **/
				if (isAvailable) { // Linux平台用Epoll模式
					bootstrap.channel(EpollServerSocketChannel.class);
				} else {
					bootstrap.channel(NioServerSocketChannel.class);
				}
				bootstrap.option(ChannelOption.SO_BACKLOG, 1024) // 服务端可连接队列数,对应TCP/IP协议listen函数中backlog参数
				.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)// 缓存区 池化操作
				.childOption(ChannelOption.TCP_NODELAY, true)// 立即写出
				.childOption(ChannelOption.SO_KEEPALIVE, true)// 长连接
				.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)// 缓存区 池化操作 关键是这句
				.childHandler(new ServerChannelInitializer());
				/** 绑定端口同步操作 */
				ChannelFuture future = bootstrap.bind(port).sync();
				
				if (future.isSuccess()) {
					log.info("启动 Netty 成功");
				}
	}
	/**
	 * 	销毁
	 */
	@PreDestroy
	public void destroy() {
		bossGroup.shutdownGracefully().syncUninterruptibly();
		workerGroup.shutdownGracefully().syncUninterruptibly();
		// businessGroup.shutdownGracefully().syncUninterruptibly();
		log.info("关闭 Netty 成功");
	}
}
